import logging
import sys
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Any, Dict, Optional

from fastapi import FastAPI, Request, Response
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint

# Constants
LOG_DIR = Path(__file__).parent / 'log'
API_LOG = LOG_DIR / 'api.log'
INFO_LOG = LOG_DIR / 'info.log'
ERROR_LOG = LOG_DIR / 'error.log'

# Create log directory and files if they don't exist
LOG_DIR.mkdir(parents=True, exist_ok=True)
for log_file in [API_LOG, INFO_LOG, ERROR_LOG]:
    if not log_file.exists():
        log_file.touch()

LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
MAX_BYTES = 10 * 1024 * 1024  # 10 MB
BACKUP_COUNT = 5


def setup_logger(name: str, log_file: Path, level: int = logging.INFO) -> logging.Logger:
    """Configure and return a logger with file and console handlers.

    Args:
        name: Unique name for the logger
        log_file: Path to the log file
        level: Logging level (default: INFO)

    Returns:
        Configured logger instance
    """
    logger = logging.getLogger(name)
    logger.setLevel(level)
    
    # Avoid duplicate handlers
    if logger.handlers:
        return logger
        
    # File handler with rotation
    file_handler = RotatingFileHandler(
        log_file,
        maxBytes=MAX_BYTES,
        backupCount=BACKUP_COUNT,
        encoding='utf-8'
    )
    file_handler.setFormatter(logging.Formatter(LOG_FORMAT))
    logger.addHandler(file_handler)
    
    # Console handler for development
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(logging.Formatter(LOG_FORMAT))
    logger.addHandler(console_handler)
    
    return logger


# Create loggers
api_logger = setup_logger('api', API_LOG)
info_logger = setup_logger('info', INFO_LOG)
error_logger = setup_logger('error', ERROR_LOG, level=logging.ERROR)


def get_request_info(request: Request) -> Dict[str, Any]:
    """Extract relevant information from a request.

    Args:
        request: FastAPI request object

    Returns:
        Dictionary with request details
    """
    return {
        'method': request.method,
        'url': str(request.url),
        'client': request.client.host if request.client else None,
        'headers': dict(request.headers),
        'path_params': request.path_params,
        'query_params': dict(request.query_params)
    }


class LoggingMiddleware(BaseHTTPMiddleware):
    """Middleware for logging requests and responses in FastAPI applications."""
    
    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        # Log the incoming request
        request_info = get_request_info(request)
        api_logger.info(f'Request: {request_info}')
        
        try:
            # Process the request
            response = await call_next(request)
            
            # Log the response
            api_logger.info(
                f'Response: status_code={response.status_code}, '
                f'headers={dict(response.headers)}'
            )
            return response
            
        except Exception as e:
            # Log any unhandled exceptions
            error_detail = {
                'request': request_info,
                'exception': str(e),
                'exception_type': type(e).__name__
            }
            error_logger.exception(f'Unhandled exception: {error_detail}')
            raise  # Re-raise the exception


def configure_logging(app: FastAPI) -> None:
    """Configure application-wide logging.

    Args:
        app: FastAPI application instance
    """
    # Add the logging middleware
    app.add_middleware(LoggingMiddleware)
    
    # Log application startup
    info_logger.info('Application starting up')
    
    # Add shutdown event handler
    @app.on_event('shutdown')
    async def shutdown_event():
        info_logger.info('Application shutting down')


# Utility functions for logging from other modules
def log_info(message: str, module: Optional[str] = None) -> None:
    """Log an informational message.

    Args:
        message: Log message
        module: Optional module name for context
    """
    prefix = f'[{module}] ' if module else ''
    info_logger.info(f'{prefix}{message}')


def log_error(message: str, exc_info: bool = False, module: Optional[str] = None) -> None:
    """Log an error message.

    Args:
        message: Error message
        exc_info: Whether to include exception info
        module: Optional module name for context
    """
    prefix = f'[{module}] ' if module else ''
    error_logger.error(f'{prefix}{message}', exc_info=exc_info)


def log_api(message: str, module: Optional[str] = None) -> None:
    """Log an API-related message.

    Args:
        message: API log message
        module: Optional module name for context
    """
    prefix = f'[{module}] ' if module else ''
    api_logger.info(f'{prefix}{message}')
